// Copyright 2012 Jay Young. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview
 *
 * A class for running a set of javascript files through the Closure Compiler
 * and returning the root AST node.
 */


importPackage(com.google.javascript.jscomp);
importPackage(com.google.javascript.rhino);


var externs = CommandLineRunner.getDefaultExterns(),
    options = new CompilerOptions(),
    logger = require('ringo/logging').getLogger('Parser');

options.ideMode = true;
WarningLevel.VERBOSE.setOptionsForWarningLevel(options);
CompilationLevel.ADVANCED_OPTIMIZATIONS
    .setOptionsForCompilationLevel(options);



/**
 * Parser class which takes an array of filenames and generates an array of
 * SCRIPT nodes.  Files are parsed in batches, the size of which can optionally
 * be passed to this constructor.
 *
 * @param {boolean} verbose Whether to log lots of status messages.
 *
 * @constructor
 */
var Parser = exports.Parser = function(verbose) {
  /**
   * @type {boolean}
   * @private
   */
  this.verbose_ = verbose;
};


/**
 * Parse the input fileset and print the resulting ASTs.
 *
 * @param {Array.<string>} fileset The array of file names to parse.
 */
Parser.prototype.printAST = function(fileset) {
  var {scripts} = this.parse(fileset);

  for (var i = 0, len = scripts.length; i < len; ++i) {
    print(scripts[i].toStringTree());
  }
};


/**
 * Parse the given files.
 *
 * @param {Array.<string>} fileset The array of file names to parse.
 * @return {{scripts: Array.<Node>, typeRegistry: JSTypeRegistry}}
 *    An array of SCRIPT nodes.
 */
Parser.prototype.parse = function(fileset) {
  logger.info('Parsing input files.');
  var {root, typeRegistry} = this.parseBatch(fileset),
      synthBlock = root.getChildAtIndex(1), // First child is externs AST
      scripts = synthBlock.children().iterator(),
      scriptNodes = [],
      scriptNames = [],
      Node = com.google.javascript.rhino.Node;
      
  while (scripts.hasNext()) {
    var script = scripts.next();
    script.detachFromParent();
    scriptNodes.push(script);
    scriptNames.push(script.getProp(Node.STATIC_SOURCE_FILE).toString());
  }
  
  var inputCount = fileset.length,
      ignored = inputCount - scriptNodes.length,
      verbose = this.verbose_;
  logger.info('Parsing complete.  ' + ignored + ' of ' + inputCount +
              (inputCount === 1 ? ' input skipped' : ' inputs skipped') +
              (verbose ? ':' : '.'));
  
  if (this.verbose_) {
    for (var i = 0, len = fileset.length; i < len; ++i) {
      if (scriptNames.indexOf(fileset[i]) === -1) {
        logger.info('\t' + fileset[i]);
      }
    }
  }

  return {
    scripts: scriptNodes,
    typeRegistry: typeRegistry
  };
};


/**
 * Parse an array of files and return the resulting AST root.
 *
 * @param {Array.<string>} fileset The files to parse.
 * @return {{root: Node, typeRegistry: JSTypeRegistry}} The AST root node.
 */
Parser.prototype.parseBatch = function(fileset) {
  var input = new java.util.ArrayList();

  for (var i = 0, len = fileset.length; i < len; ++i) {
    input.add(JSSourceFile.fromFile(fileset[i]));
  }

  var compiler = new Compiler();
  compiler.init(externs, input, options);
  compiler.parse();

  return {
    root: compiler.getRoot(),
    typeRegistry: compiler.getTypeRegistry()
  };
};
